// SPDX-License-Identifier: GPL-2.0
#ifndef _TNL_EXPAND_H
#define _TNL_EXPAND_H

#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/gre.h>
#include <net/ip_tunnels.h>
#include <net/ip6_tunnel.h>

#ifdef CONFIG_ONT_NET_FEATURES

enum tnl_gre_hook {
	TNL_GRE_HOOK_NONE,
	TNL_GRE_RCV_RECORD_HOOK,
	TNL_GRE_PRE_XMIT_IP_HOOK,
	TNL_GRE_PRE_XMIT_IP6_HOOK,
	TNL_GRE_XMIT_REFRESH_HOOK,
	TNL_GRE_XMIT_ACCEL_HOOK,
	TNL_GRE_HOOK_MAX
};

struct tnl_hook_map {
	enum tnl_gre_hook type;
	const char *desc;
	void **func;
};

bool tnl_gre_rcv_seq_hook_valid(void);
bool tnl_gre_rcv_seq_handle(struct sk_buff *skb, __be16 flags, u32 seqno, __be16 tnl_flags);
bool tnl_gre_xmit_header_build(struct sk_buff *skb, struct ip_tunnel *tunnel,
			       const struct iphdr *tnl_params, __be16 proto);
bool tnl_gre6_xmit_header_build(struct sk_buff *skb, struct ip6_tnl *tunnel,
				__be16 proto, __u8 *dsfield);
bool tnl_gre_xmit_pkt_handle(struct sk_buff *skb, struct net_device *dev);
bool tnl_gre6_xmit_pkt_handle(struct sk_buff *skb, struct net_device *dev);

void tnl_gre_pmtu_disc_handle(struct iphdr *tnl_params);
void tnl_gre_tap_set_tcpmss(struct sk_buff *skb, struct net_device *dev,
			    __be16 proto, bool is_rcv, uint32_t af_type);

static inline __be16
ip_tunnel_get_proto(struct sk_buff *skb, struct net_device *dev, const struct iphdr *tnl_params)
{
	struct ip_tunnel *tunnel = netdev_priv(dev);
	struct gre_base_hdr *greh;
	struct vlan_hdr *vlanh;
	__be16 protocol = skb->protocol;

	/* Use inner vlan protocol for softgre. */
	if (tnl_params->protocol == IPPROTO_GRE && skb->protocol == htons(ETH_P_8021Q)) {
		greh = (struct gre_base_hdr *)skb->data;
		if (greh->protocol == htons(ETH_P_TEB)) {
			/* only one-layer vlan is supported. */
			vlanh = (struct vlan_hdr *)(skb->data + tunnel->hlen +
						    sizeof(struct ethhdr));
			protocol = vlanh->h_vlan_encapsulated_proto;
		}
	}

	return protocol;
}

/* No fragment to inner ipv4 payload for dslite tunnel. */
static inline void ip6_tunnel_set_fragment(struct sk_buff *skb)
{
	nf_reset_ct(skb);
	skb->ignore_df = 1;
	skb->protocol = htons(ETH_P_IPV6);
	skb->dev = skb_dst(skb)->dev;
}

#else /* CONFIG_ONT_NET_FEATURES */

static inline bool tnl_gre_rcv_seq_hook_valid(void)
{
	return false;
}

static inline bool
tnl_gre_rcv_seq_handle(struct sk_buff *skb, __be16 flags, u32 seqno, __be16 tnl_flags)
{
	return false;
}

static inline bool
tnl_gre_xmit_header_build(struct sk_buff *skb, struct ip_tunnel *tunnel,
			  const struct iphdr *tnl_params, __be16 proto)
{
	return false;
}

static inline bool
tnl_gre6_xmit_header_build(struct sk_buff *skb, struct ip6_tnl *tunnel,
			   __be16 proto, __u8 *dsfield)
{
	return false;
}

static inline bool tnl_gre_xmit_pkt_handle(struct sk_buff *skb, struct net_device *dev)
{
	return false;
}

static inline bool tnl_gre6_xmit_pkt_handle(struct sk_buff *skb, struct net_device *dev)
{
	return false;
}

static inline void tnl_gre_pmtu_disc_handle(struct iphdr *tnl_params)
{
}

static inline void
tnl_gre_tap_set_tcpmss(struct sk_buff *skb, struct net_device *dev,
		       __be16 proto, bool is_rcv, uint32_t af_type)
{
}

static inline __be16
ip_tunnel_get_proto(struct sk_buff *skb, struct net_device *dev, const struct iphdr *tnl_params)
{
	return skb->protocol;
}

static inline void ip6_tunnel_set_fragment(struct sk_buff *skb)
{
}

#endif /* CONFIG_ONT_NET_FEATURES */

#endif /* _TNL_EXPAND_H */
